/**
 *  
 */
package time.providers.sql.query;
import time.compiler.query.Data;
import time.compiler.types.Data;

typedef SqlQuery =
{
	select:Null<Array<SqlSelect>> , //at the end this must not be null
	from:Array<SqlFrom>,
	where:Null<SqlExpr>,
	aggregator:Null<SqlAggregator>,
	having:Null<SqlExpr>,
	order:Null<SqlOrderBy>,
	limit:Null<SqlLimit>,
	
	///////// VARIABLES ONLY USED FOR PARSING ////////////
	level:Int, //needed to be able to know where the variable comes from
}

//why a condition on the select? Because we can do stuff like this:
//Test.map(function(t) t.a == "Test"); , and it should properly return an array of bools
typedef SqlSelect = { expr:SqlExpr, name:Null<String> };

typedef SqlFrom =
{
	alias:String,
	from:SqlJoinSource,
	joins:Array<{alias:String, join:SqlJoin}>
}

enum SqlJoin
{
	SClassicJoin(leftField:Variable, rightField:Variable, joinOp:SqlJoinOp, tbl:SqlJoinSource);
	SExprJoin(expr:SqlExpr, joinOp:SqlJoinOp, tbl:SqlJoinSource);
}

enum SqlJoinOp
{
	InnerJoin;
	LeftJoin;
	RightJoin;
}

enum SqlJoinSource
{
	STable(table:DBType);
	SInternalTable(name:String);
	SQuery(query:SqlQuery);
}

enum SqlExpr
{
	SBinop(op:QueryOp, cond1:SqlExpr, cond2:SqlExpr);
	SUnop(op:QueryUnop, exp:SqlExpr);
	SValue(v:SqlValue);
	SPar(e:SqlExpr);
}

enum SqlValue
{
	VExternalVar(name:String);
	VVariable(v:Variable);
	VInt(i:Int);
	VFloat(f:Float);
	VString(s:String);
	VBool(b:Bool);
	VNull;
}

enum Variable
{
	VColumn(tableAlias:String, columnName:String);
	//what's the difference between VColumn and VStruct? VStruct is not named Value Type since all the basic types would be declared as VColumn. VStruct means only references to structs that are defined as one page really
	VStruct(tableAlias:String, columns:Array<String>, valueColumn:Null<String>);
	//the table alias
	VAlias(alias:String);
	//this is specially for SQL variables. They normally are operations like SUM(x) made. Do not mistaken this with the "soft" variables available at the query language. They always get translated into SqlExpr at generation
	VName(i:String);
}

typedef SqlAggregator =
{
	aggregateBy:Array<Variable>,
	aggregators:Array<Aggregator>,
	locked:Bool
}

typedef Aggregator =
{
	aggregatorFunc:AggregatorFunction,
	columnVar:Variable,
	varName:String //will become a VName
}

enum AggregatorFunction
{
	AAvg;
	AMax;
	AMin;
	ACount;
	ASum;
	ACustom(s:String);
}

enum SqlOrder
{
	SASC;
	SDESC;
}

typedef SqlOrderBy = Array<{variable:Variable, order:SqlOrder}>;

typedef SqlLimit = { limitTo:Int, offset:Int };

class DataExtensions
{
	public static function map(e:SqlExpr
}

/*

var q = query(Person).filter(function (p) p.house.age > 50).map(function(p) p.company).filter(function (c) c.revenue > 4000);

=> 

select t3.* from Person t1 LEFT JOIN House t2 ON t1.house = t2.id LEFT JOIN Company t3 ON t1.company = t2.id WHERE t2.age > 50 AND t3.revenue > 4000

passos
1 - passar, identificar e dar nome para todas as relações usadas. lembrando sempre de voltar para o current_this
2 - identificar tambem todos os filtros aplicados
3 - os


var q = query(Basket).filter(function(b) b.products.exists(function (p) p.name == "Something"))

select t1.* from Basket t1 

select t1.* from Basket t1 where t1.id IN (

class product { name }
class seller { name }
class order { date, product, seller }

var q = query(order).filter(function(o) o.date < $mydate).orderBy(o.date, desc).limit(10);

var q = query(order).filter(function(o) o.date < $mydate).

se voce esta fazendo uma sel de um para muitos, e colocando um condicional no muitos, vc precisa adicionar o distinct
é isso?

NAO EXATAMENTE



o has é um caso mais complexo
var q = query(Entity).filter(function(e) return e.components.has(function(c) return c.name == "CPosition"));
                            ^ implica um distinct() -> PQ!? 
=>

select t1.* from

Entity t1 left join Entity___rel___components t2 ON t1.id = t2.Entity left join Component t3 ON t3.id = t2.Component where t3.name = "CPosition"


select t1.* from

Entity t1, (select t2.* from Entity t2 






=======================================

var q = query(Basket).filter(function(b) b.products.exisits(function(p) p.price > 1000));

quando se tem uma relação para muitos, temos que saber se vamos ficar com a relação para um ou para muitos

OU SEJA, o map tem que 



======================================

class Product { name }
class Order { products, customer }
class Customer { name }

var q = query(Order).filter(function(o) o.products.exists(function(p) p.name == "OLAAA"));

-> toda vez que se tem exists, a gente precisa de um agregador. ponto


*/